// 完成日志器模块：
//     抽象日志器基类
//     派生出不同的子类（同步日志器&异步日志器）

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "worker.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>

namespace mylog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const string &logger_name,
               LogLevel::level level,
               Formatter::ptr &formatter,
               vector<LogSink::ptr> &sinks) 
               : _logger_name(logger_name),
                _limit_level(level),
                _formatter(formatter),
                _sinks(sinks.begin(), sinks.end()) {}
        const string &name()
        {
            return _logger_name;
        }
        // 完成构造日志消息对象过程并进行格式化，得到格式化后的消息字符串--然后进行落地输出
        template<typename... Args>
        void debug(const string &file, size_t line, Args &&...args)
        {
            outPutLog(LogLevel::level::DEBUG, file, line, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void info(const string &file, size_t line, Args &&...args)
        {
            outPutLog(LogLevel::level::INFO, file, line, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void warn(const string &file, size_t line, Args &&...args)
        {
            outPutLog(LogLevel::level::WARN, file, line, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void error(const string &file, size_t line, Args &&...args)
        {
            outPutLog(LogLevel::level::ERROR, file, line, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void fatal(const string &file, size_t line, Args &&...args)
        {
            outPutLog(LogLevel::level::FATAL, file, line, std::forward<Args>(args)...);
        }

    protected:
        void outPutLog(LogLevel::level level, const string &file, size_t line, const string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，进行日志的格式化，最后落地
            // 判断当前日志是否达到了输出等级
            if (level < _limit_level)
            {
                return;
            }
            // 对fmt格式化规则字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt); // 通过fmt找到不定参的起始位置,然后把不定参存入ap指针指向的空间当中
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (-1 == ret)
            {
                cout << "vasprintf failed!" << endl;
                return;
            }
            va_end(ap); // 将ap指针置空
            // 构造LogMsg对象
            LogMsg msg(level, line, file, _logger_name, res);
            // 通过格式化工具对LosMsg进行格式化，得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 进行日志落地
            log(ss.str().c_str(), ss.str().size());
            free(res); // 因为res指针所指向的空间是vasprintf里面动态申请的，所以要对其进行释放，以防资源泄露
        }

        // 抽象接口完成实际的落地输出--不同的日志器会有不同的实际落地方式
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;
        string _logger_name;
        std::atomic<LogLevel::level> _limit_level;
        Formatter::ptr _formatter;
        vector<LogSink::ptr> _sinks;
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const string &logger_name,
                   LogLevel::level level,
                   Formatter::ptr &formatter,
                   vector<LogSink::ptr> &sinks) : Logger(logger_name, level, formatter, sinks) {}

    protected:
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
            {
                return;
            }
            for (auto &sink : _sinks)
            {
                sink->log(data, len); // 根据日志落地模块数组里的不同落地方式进行日志落地
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const string &logger_name,
                   LogLevel::level level,
                   Formatter::ptr &formatter,
                   vector<LogSink::ptr> &sinks,
                   AsyncType worker_type) : 
                   Logger(logger_name, level, formatter, sinks),
                   _worker(std::make_shared<AsyncWorker>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), worker_type)) {}
        //将数据写入缓冲区
        void log(const char *data, size_t len)
        {
            _worker->push(data, len);
        }
        //设计一个实际落地函数（将缓冲区的数据落地）
        void realLog(Buffer &buf)
        {
            if (_sinks.empty())
            {
                return;
            }
            for (auto &sink : _sinks)
            {
                sink->log(buf.begin(), buf.readableSize()); // 根据日志落地模块数组里的不同落地方式进行日志落地
            }
        }

    private:
        AsyncWorker::ptr _worker;
    };

    // 使用建造者模式来建造日志器，而不用让用户直接去构造日志器，简化用户的使用复杂度
    // 抽象一个日志器建造者类（完成日志器对象所需零部件的构造 & 日志器的构建）
    //   设置日志器类别
    //   将不同类型的日志器创建放到同一个日志器建造者类中完成
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC),
              _limit_level(LogLevel::level::DEBUG),
              _worker_type(AsyncType::ASYNC_SAFE) {}

        void buildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }

        void buildEnableUnSafeAsync()
        {
            _worker_type = AsyncType::ASYNC_UNSAFE;
        }

        void buildLoggerName(const string name)
        {
            _logger_name = name;
        }

        void buildLoggerLevel(LogLevel::level level)
        {
            _limit_level = level;
        }

        void buildFormatter(const string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }

        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }

        virtual Logger::ptr build() = 0;

    protected:
        AsyncType _worker_type;
        LoggerType _logger_type;
        string _logger_name;
        LogLevel::level _limit_level;
        Formatter::ptr _formatter;
        vector<LogSink::ptr> _sinks;
    };
    // 派生出具体的建造者类--局部日志器的建造者&全局日志器的建造者（后边添加了全局单例管理器之后将日志器添加到全局管理）
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false); // 必须得有日志器名称
            if (_formatter.get() == nullptr)       // 没有指定格式化规则
            {
                _formatter = std::make_shared<Formatter>(); // 就使用默认格式化规则
            }
            if (_sinks.empty()) // 没有落地方向
            {
                buildSink<StdoutSink>(); // 就默认落地到标准输出
            }
            if (_logger_type == LoggerType::LOGGER_ASYNC) // 如果是异步日志器
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _worker_type);
            }
            // 不是异步日志器就是同步日志器
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    class LoggerManager
    {
    public:
        static LoggerManager& getInstance()
        {
            //在C++11之后，针对静态局部变量，编译器在编译层面实现了线程安全
            //当静态局部变量在没有构造完成之前，其他线程进入的时候就会阻塞
            static LoggerManager singleton;
            return singleton;
        }
        void addLogger(Logger::ptr &logger)
        {
            if(hasLogger(logger->name())) return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        bool hasLogger(const string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end())
            {
                return false;
            }
            return true;
        }
        Logger::ptr getLogger(const string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end())
            {
                cout << "不存在名为：" + name + "的日志器" << endl;
                return Logger::ptr();
            }
            return it->second;
        }
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }
    private:
        LoggerManager() 
        {
            std::unique_ptr<mylog::LoggerBuilder> builder(new mylog::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers.insert(std::make_pair(_root_logger->name(), _root_logger));
        }
        LoggerManager(const LoggerManager &) = delete;
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;//默认日志器
        std::unordered_map<string, Logger::ptr> _loggers;
    };

    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false); // 必须得有日志器名称
            if (_formatter.get() == nullptr)       // 没有指定格式化规则
            {
                _formatter = std::make_shared<Formatter>(); // 就使用默认格式化规则
            }
            if (_sinks.empty()) // 没有落地方向
            {
                buildSink<StdoutSink>(); // 就默认落地到标准输出
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC) // 如果是异步日志器
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _worker_type);
            }
            else// 不是异步日志器就是同步日志器
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}
#endif